iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0
Modern Web

30 days of React 系列 第 28

Day 28 - React useEffect 基礎:Focus a field on mount 實作練習

  • 分享至 

  • xImage
  •  

今天讀完了 React 官方文件的 Synchronizing with Effects 的章節,初步學習了 Effects 的相關知識,透過實作來練習和理解觀念吧。

透過今天的練習會學習到的內容:

  • 什麼是副作用
  • 如何使用 useEffect
  • 依賴(dependencies)的功能是什麼
  • 有無設定(dependencies)的意義是什麼

* 本篇還不會提到 cleanup 的操作

Focus a field on mount

今天的練習也是出自官方文件。這個練習要讓 input 欄位顯示在focus 的狀態,並可以馬上輸入的狀態。也就是讓畫面從這樣子:

變成這樣子:

現有程式碼:

import { useEffect, useRef } from "react";

export default function MyInput({ value, onChange }) {
  const ref = useRef(null);

  // TODO: This doesn't quite work. Fix it.
  // ref.current.focus()

  return <input ref={ref} value={value} onChange={onChange} />;
}

原始的程式碼使用ref.current.focus()但並未成功。

問題點

使用ref.current.focus()不成功的的原因是這個屬於「副作用」(side effect),在 React 元件內部,我們定義了樣式、事件處理程序、狀態等等,這些都是 React 虛擬 DOM 的一部分。當元件的狀態(state)或屬性(props)發生變化時,React 會根據這些變化重新計算虛擬 DOM 的內容。

然而,當我們需要直接操作瀏覽器的 DOM 時,例如設置焦點、改變元素樣式、訂閱事件等,這就牽涉到了和外部世界(實際的瀏覽器 DOM)的交互。這些操作是 React 無法自動追蹤和管理的,因為它們不在 React 虛擬 DOM 的範疇內。因此,這些操作被稱為副作用。

該如何處理

在 React 當中處理副作用時我們應該使用的是useEffect 。useEffect 的 syntax 是這樣子:

useEffect(() => {
  // 執行副作用操作的程式碼

  return () => {
    // 在組件被卸載(unmount)時執行的清理操作
  };
}, [dependencies]);

因此僅需要將目前程式碼的ref.current.focus()

放進useEffect function 當中並,設定依賴(dependencies) 為[]empty string。即可完成這項小練習。

依賴(dependenies)是什麼

依賴(dependencies)的功能是限制 useEffect 的觸發,當有設定 dependencies 的情況下,程式碼會在依賴陣列的值發生變化時執行。

譬如假設我們設定了二個 dependencies:

useEffect(() => {
  // 這裡的程式碼將在依賴陣列中的值發生變化時執行
}, [dependency1, dependency2]);

只有當 dependency1dependency2 中的任何一個值發生變化時,useEffect 中的程式碼才會被執行。這樣可以精確地控制副作用的執行時機,確保它們只在特定的依賴值發生變化時執行,而不是在每次渲染時都執行。

那回到練習中完成的程式碼,在這邊我們將依賴(dependencies)設定為 empty string 的意義是什麼呢?

import { useEffect, useRef } from "react";

export default function MyInput({ value, onChange }) {
  const ref = useRef(null);

  // TODO: This doesn't quite work. Fix it.
  // ref.current.focus()
  useEffect(() => {
    ref.current.focus();
  }, []);

  return <input ref={ref} value={value} onChange={onChange} />;
}

當我們將 dependencies 設定為 empty string 它代表的是「程式碼僅在掛載(mounting)時執行(當元件出現時)」。也就是說空陣列表示沒有依賴,只在組件第一次渲染時執行。

空陣列依賴的意義

如果沒有依賴,而依賴也是選擇性(optional)參數並非必要,那麼直接不要寫 dependencies 讓程式碼維持這樣不好嗎?

useEffect(() => {
  ref.current.focus();
});

還有將這個空陣列寫出來的意義嗎?

這的確是相當容易混淆的地方,React 官方文件也針對這點提供了相關的說明(見 pitfall 處):

useEffect(() => {
  // 這會在每次渲染後執行
});

useEffect(() => {
  // 這僅在掛載時執行(當組件出現時)
}, []);

useEffect(() => {
  // 這在掛載時執行 *並且* 在上一次渲染後如果 a 或 b 有變化時也會執行
}, [a, b]);

假設如果不設定依賴陣列,useEffect 會在每次渲染後都執行, ref.current.focus() 將會每次都被呼叫,導致 input 在每次渲染後都被聚焦(focused),而不僅僅是元件掛載(mounting)時,所以useEffect 中的 ref.current.focus() 操作在每次組件重新渲染時都被執行,即使 input 元素已經聚焦,也會再次被聚焦。這樣子可能會造成不必要的網頁負擔和不必要的 DOM 更新。

以上是今天的學習,目前還沒有提到 clean up 的概念,明天會繼續看其他的例子來學習 useEffect 的應用。

參考資料

  • React 官方文件 - Synchronizing with Effects

上一篇
Day 27 - React Ref 實作練習
下一篇
Day 29 - React useEffect cleanup function 練習:製作電子時鐘
系列文
30 days of React 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言